/**************************************************************************
 *** 程序作者 :															 ***
 *** 日    期 :															 ***
 *** 所属模块 :															 ***
 *** 程序名称 : ipcsvr_comm.c                                            ***
 *** 程序作用 : ipc服务器客户端处理公共模块                              ***
 *** 程序功能 :                                                          ***
 ***            ipc_bind     监听绑定函数，将监听绑定到文件名            ***
 ***            ipc_accept   接受客户端连接请求                          ***
 ***            ipc_conn     客户端连接请求                              ***
 *** 使用注意 :                                                          ***
 *** 修改记录 :                                                          ***
 ***************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ipcsvr.h"
#include "pub_log.h"

#define CLI_PERM        S_IRWXU

/**********************************************************************
 * 函 数 名：  ipc_bind
 * 函数功能：
 *             监听绑定函数，服务器将监听绑定到指定的文件名
 * 作    者：  gujingyu
 * 完成时间：  2010年07月16日
 *
 * 参    数：
 *     输  入:
 *		name 绑定文件文件名
 *               lsncnt  允许客户端的最大数量
 *     输  出:
 *
 * 返 回 值：
 *            >0   连接成功,返回监听文件描述符
 *            <0   失败
 *                  -1:创建unix域套接口失败
 *                  -2:unix域套接口绑定错误
 *                  -3:unix域套接口监听错误
 **********************************************************************/
int ipc_bind(char *filename, int lsncnt)
{
    int    lsn_fd;
    int    len, iRc;
    struct sockaddr_un        un;   /* struct  of  AF_UNIX  socket address**/

    if(filename == NULL || strlen(filename) == 0 )
    {
        return -1;
    }

    if( lsncnt <= 0 )    /** set default lsncnt  200 **/
    {
        lsncnt = 200;
    }

    lsn_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if ( lsn_fd < 0 )
    {
        return -1;
    }

    unlink(filename);        /* in case it already exists */

    memset(&un, 0, sizeof(un));
    un.sun_family = AF_UNIX;

    strncpy(un.sun_path, filename, sizeof(un.sun_path) - 1);

    len = sizeof(struct sockaddr_un);

    iRc = bind(lsn_fd, (struct sockaddr *)&un, len);
    if(iRc)
    {
        iRc = -2;
        goto ERROR;
    }

    iRc = listen(lsn_fd, lsncnt);
    if (iRc)         /* tell kernel we're a server */
    {
        iRc = -3;
        goto ERROR;
    }

    return(lsn_fd);

ERROR:
    if( lsn_fd >= 0 )
    {
        close(lsn_fd);
    }
    return(iRc);
}
int tcp_bind(char *filename, int lsncnt)
{
    char sFile[512];
    memset(sFile, 0x00, sizeof(sFile));
    if(filename == NULL)
    {
        return -1;
    }

    if( lsncnt <= 0 )    /** set default lsncnt  200 **/
    {
        lsncnt = 200;
    }
    if(getenv("SWWORK") != NULL)
    {
        sprintf(sFile, "%s/tmp/.%s.file", getenv("SWWORK"), filename);
    }
    else
    {
        return -1;
    }

    return ipc_bind(sFile, lsncnt);
}
/**********************************************************************
 * 函 数 名：  ipc_accept
 * 函数功能：  接受客户端连接请求，返回连接的文件描述符
 * 作    者：  gujingyu
 * 完成时间：  2010年07月16日
 *
 * 参    数：
 *     输  入:
 *             lsnfd   监听文件描述符
 *     输  出:
 *             puid
 * 返 回 值：
 *            fd   连接成功,返回连接文件描述符
 *            <0   失败
 *                  -1:unix域套接口接受失败
 *                  -2:获取连接信息错
 *                  -3:检查权限错
 *                  -4:连接超时失效
 **********************************************************************/
int ipc_accept(int lsnfd, uid_t *puid)
{
    int        clifd, iRc, len;
    time_t     staletime;
    struct sockaddr_un   un;
    struct stat          statbuf;

    len = sizeof(struct sockaddr_un);

    while(1)
    {
        memset( &un, 0x00, sizeof(struct sockaddr_un));
        clifd = accept(lsnfd, (struct sockaddr *)&un, (socklen_t *)&len);
        if( clifd >= 0 )
        {
            break;
        }

        if( errno == EINTR ) /* often errno=EINTR, if signal caught */
        {
            continue;
        }

        return -1;
    }

    return(clifd);

ERROR:
    if( clifd > 0 )
    {
        close(clifd);
    }
    return(iRc);
}

/**********************************************************************
 * 函 数 名：  ipc_conn
 * 函数功能：
 *             客户端连接请求函数
 * 作    者：  gujingyu
 * 完成时间：  2010年07月16日
 *
 * 参    数：
 *     输  入:
 *             cliname    客户端绑定文件名
 *             svrname    服务器监听文件名
 *
 *     输  出:
 *
 * 返 回 值：
 *             >0   连接FIFO文件描述符
 *             <0   连接失败错误符
 *                  -1:创建unix域套接口失败
 *　　　　　　　　　-2:填充unix域套接口结构体错误
 *                  -3:unix域套接口绑定错误
 *                  -4:unix域套接口连接错误
 *                  -5:改变文件权限错误
 **********************************************************************/
int ipc_conn(char *cliname, char *svrname)
{
    int  clifd;
    int  len, iRc;
    struct sockaddr_un unix_addr;

    clifd = socket(AF_UNIX, SOCK_STREAM, 0);
    if( clifd < 0)
    {
        return -1;
    }

    memset(&unix_addr, 0x00, sizeof(struct sockaddr_un));
    unix_addr.sun_family = AF_UNIX;
    strncpy(unix_addr.sun_path, cliname, sizeof(unix_addr.sun_path) - 1);

    len = sizeof(struct sockaddr_un);

    unlink(unix_addr.sun_path);     /* in case it already exists */

    iRc = bind(clifd, (struct sockaddr *)&unix_addr, len);
    if( iRc < 0 )
    {
        iRc = -3;
        goto ERROR;
    }

    iRc = chmod(unix_addr.sun_path, CLI_PERM);
    if( iRc < 0)
    {
        iRc = -5;
        goto ERROR;
    }

    memset(&unix_addr, 0x00, sizeof(unix_addr));
    unix_addr.sun_family = AF_UNIX;
    strncpy(unix_addr.sun_path, svrname, sizeof(unix_addr.sun_path) - 1);

    len = sizeof(struct sockaddr_un);

    iRc = connect(clifd, (struct sockaddr *)&unix_addr, len);
    if( iRc < 0 )
    {
        iRc = -4;
        goto ERROR;
    }

    return clifd;
ERROR:
    if( clifd >= 0 )
    {
        close(clifd);
    }
    return iRc;
}
int tcp_conn(char *cliname, char *svrname)
{
    char sFile[256];
    char sTmp[256];
    memset(sFile, 0x00, sizeof(sFile));
    memset(sTmp, 0x00, sizeof(sTmp));

    if(cliname == NULL || strlen(cliname) == 0 || svrname == NULL || strlen(svrname) == 0)
    {
        return -1;
    }
    if(getenv("SWWORK") != NULL)
    {
        sprintf(sFile, "%s/tmp/.%s.file", getenv("SWWORK"), cliname);
        sprintf(sTmp, "%s/tmp/.%s.file", getenv("SWWORK"), svrname);
    }
    else
    {
        return -1;
    }

    return ipc_conn(sFile, sTmp);
}
/**********************************************************************
 * 函 数 名：  ipc_udpbind
 * 函数功能：
 *             监听绑定函数，将监听绑定到指定的文件名
 * 作    者：  zhang hailu
 * 完成时间：  2010年10月26日
 *
 * 参    数：
 *     输  入:
 *		svrfile 绑定文件文件名
 *               iMax  允许客户端的最大数量
 *     输  出:
 *
 * 返 回 值：
 *            >0   连接成功,返回监听文件描述符
 *            <0   失败
 *                  -1:unix域输入文件名错误
 *                  -2:创建unix域套接口失败
 *                  -3:unix域套接口绑定错误
 **********************************************************************/
int ipc_udpbind(char *svrfile)
{
    int lsnfd;
    int len, iRc;
    struct sockaddr_un server_addr;

    lsnfd = 0;
    len = 0;
    iRc = -1;

    if(svrfile == NULL || strlen(svrfile) == 0 )
    {
        return -1;
    }

    lsnfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if( lsnfd < 0)
    {
        return -2;
    }

    unlink(svrfile);
    memset(&server_addr, 0x00, sizeof(struct sockaddr_un));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, svrfile, sizeof(server_addr.sun_path) - 1);
    len = strlen(server_addr.sun_path) + sizeof(server_addr.sun_family) + 1;

    iRc = bind(lsnfd, (struct sockaddr *)&server_addr, len);
    if(iRc < 0)
    {
        slog_err("errno = [%d][%s]", errno, strerror(errno));
        return -3;
    }

    return(lsnfd);
}

/**********************************************************************
 * 函 数 名：  ipc_UdpRecv
 * 函数功能：
 *             接收数据函数
 * 作    者：  zhang hailu
 * 完成时间：  2010年11月4日
 *
 * 参    数：
 *     输  入:
 *             clifd    连接文件描述符
 *	      iTimeout	超时时间（秒）
 *	      iLen 	准备接收的数据长度
 *     输  出: sBuf     接收到数据
 *
 * 返 回 值：
 *             0>  接收成功
 *             <0   接收失败
 *　　　　　　　　　-1:输入参数错误
 *                  -2:select 错误
 *                  -3:接收数据错误
 *                  -4:时钟中断 超时没有数据到达
 **********************************************************************/
int  ipc_udprecv(int clifd, int iTimeout, char *sBuf, int iLen)
{
    int	iRc;
    int	fromlen;
    fd_set	fd_read;
    struct sockaddr_un from;
    struct timeval  time_out;

    iRc = -1;
    fromlen = 0;
    FD_ZERO(&fd_read);
    FD_SET((unsigned int)clifd, &fd_read);
    time_out.tv_sec = iTimeout;
    time_out.tv_usec = 0;
    memset(&from, 0x00, sizeof(struct sockaddr_un));
    from.sun_family = AF_UNIX;

    if( clifd < 0 || iLen < 0 )
    {
        return -1;;
    }

    iRc = select( clifd + 1, &fd_read, NULL, NULL, &time_out);
    if( iRc == 0 )/**超时**/
    {
        return -4;
    }
    else if( iRc < 0 )
    {
        if(errno == EINTR) /**时钟中断**/
        {
            return -4;
        }
        else
        {
            return -2;
        }
    }
    if(FD_ISSET(clifd, &fd_read) )
    {
        iRc = recvfrom(clifd, sBuf, iLen, 0, (struct sockaddr *)&from, (socklen_t *)&fromlen);
        if( iRc <= 0 )
        {
            return -3;
        }
    }
    return iRc;
}

/**********************************************************************
 * 函 数 名：  ipc_udpsend
 * 函数功能：
 *             服务器接收数据函数
 * 作    者：  zhang hailu
 * 完成时间：  2010年10月27日
 *
 * 参    数：
 *     输  入:
 *             	clifd    	连接文件描述符
 *	      	filename	发送目的
 *		sBuf     	发送数据
 *		iLen		发送数据长度
 *     输  出:   无
 *
 * 返 回 值：
 *             0>   发送成功
 *             <0   接收失败
 *　　　　　　　　　 -1:输入参数错误
 *                  -2:发送失败
 **********************************************************************/
int  ipc_udpsend(int clifd, char *filename, char *sBuf, int iLen )
{
    int	iRc;
    int	iTolen;
    struct sockaddr_un stTo;

    iRc = -1;
    iTolen = 0;
    memset(&stTo, 0x00, sizeof(stTo));
    stTo.sun_family = AF_UNIX;

    if(clifd < 0 || iLen < 0 || filename == NULL || strlen(filename) == 0)
    {
        return -1;
    }
    strncpy(stTo.sun_path, filename, sizeof(stTo.sun_path) - 1);
    iTolen = strlen(stTo.sun_path) + sizeof(stTo.sun_family) + 1;

    iRc = sendto(clifd, sBuf, iLen, 0, (struct sockaddr *)&stTo, iTolen);
    if( iRc <= 0 )
    {
        return -2;
    }
    return iRc;
}

int udp_bind(char *pName)
{
    char sFile[256];
    memset(sFile, 0x00, sizeof(sFile));

    if(pName == NULL || strlen(pName) == 0)
    {
        return -1;
    }
    sprintf(sFile, "%s/tmp/.%s_.file", getenv("SWWORK"), pName);

    return ipc_udpbind(sFile);
}

/**********************************************************************
 * 函 数 名：  udp_send
 * 函数功能：
 *             接收数据公共函数
 * 作    者：  liteng
 * 完成时间：  2010年11月5日
 * 参    数：
 *     输  入:
 *             	fd    		连接文件描述符
 *		pBuf     	发送数据缓冲区
 *     		iLen		发送数据长度
 *	      	pName		发送目的
 *     输  出:
 * 返 回 值：
 *             0>   发送成功
 *             <0   接收失败
 *                  -2:发送失败
 **********************************************************************/
int udp_send(int fd, char *pBuf, int iLen, char *pName)
{
    char sFileName[512];
    memset(sFileName, 0x00, sizeof(sFileName));

    if( fd < 0 || iLen < 0 || pName == NULL || strlen(pName) == 0 )
    {
        return -1;
    }

    if(getenv("SWWORK") == NULL)
    {
        return -1;
    }
    else
    {
        sprintf(sFileName, "%s/tmp/.%s_.file", getenv("SWWORK"), pName);
    }
    return  ipc_udpsend(fd, sFileName, pBuf, iLen);
}


/**********************************************************************
 * 函 数 名：  udp_recv
 * 函数功能：
 *             接收数据公函数
 * 作    者：  liteng
 * 完成时间：  2010年11月5日
 * 参    数：
 *     输  入:
 *             fd    	连接文件描述符
 *	      iLen	准备接收数据的长度
 *     输  出: sBuf      接收数据缓冲区
 * 返 回 值：
 *             0>  接收成功
 *             <0   接收失败
 *　　　　　　　　　-1:输入参数错误
 *                  -2:select 错误
 *                  -3:接收数据错误
 *                  -4:时钟中断 超时没有数据到达
 **********************************************************************/
int udp_recv(int fd, char *pBuf, int iLen)
{
    return ipc_udprecv(fd, 3, pBuf, iLen);
}


/**********************************************************************
 * 函 数 名：  iAfSockSend
 * 函数功能：
 *             AfSock发送数据公共函数
 * 作    者：  liteng
 * 完成时间：  2011年5月13日
 * 参    数：
 *     输  入:
 *             iFd    	连接文件描述符
 *	      psBuf	发送缓冲区
 *	      iLen	准备发送数据的长度
 *	      iFlag	发送标识
 *     输  出: 无
 * 返 回 值：
 *             >=0  发送的字节数
 *             <0   发送失败
 *　　　　　　　　　-32:Broken pipe
 **********************************************************************/
int iAfSockSend(int iFd, char *psBuf, int iLen, int iFlags)
{
    int iRc;
    char sTmp[1000];
    errno = 0;

    if( iFd <= 0 || psBuf == NULL )
    {
        return -1;
    }

    iRc = -1;
    iRc = send(iFd, psBuf, iLen, iFlags);
    if( iRc < 0 )
    {
        if( errno == 32 )				/* Broken pipe                     */
        {
            slog_err("send error[%s],iRc=[%d]", strerror(errno), iRc);
            return -32;
        }
        iRc = -1;
        iRc = send(iFd, psBuf, iLen, iFlags);
        if( iRc < 0 )
        {
            slog_err("send error[%s],iRc=[%d]", strerror(errno), iRc);
            return -1;
        }
    }
    return iRc;
}


/**********************************************************************
 * 函 数 名：  iAfSockSecv
 * 函数功能：
 *             AfSock接收数据公共函数
 * 作    者：  liteng
 * 完成时间：  2011年5月13日
 * 参    数：
 *     输  入:
 *             iFd    	连接文件描述符
 *	      iLen	准备接收数据的长度
 *	      iFlag	接收标识
 *     输  出: psBuf	接收缓冲区
 * 返 回 值：
 *             >=0  接收的字节数
 *             <0   接收失败
 *　　　　　　　　　-32:Broken pipe
 **********************************************************************/
int iAfSockRecv(int iSocketId, char *buf, int length, int iFlag)
{
    if(iSocketId <= 0 || buf == NULL || length <= 0)
    {
        slog_err("param error");
        return -1;
    }

    int iRc, iSubLen;

    iRc = 0;
    iSubLen = 0;
    int times = 0;
    while(1)
    {
        iRc = 0;
        iRc = recv(iSocketId, buf + iSubLen, length - iSubLen, iFlag);
        if(iRc < 0)
        {
            if(errno == EINTR)
            {
                continue;
            }
            slog_err("接收失败,error,iRc=[%d],sockid=[%d],len=[%d],sublen=[%d]", iRc, iSocketId, length, iSubLen);
            return(iRc);
        }
        else if(iRc == 0)
        {
            times++;
            if(times > 3)
            {
                slog_err("接收长度不足[%d] [%d]", iSubLen, length);
                return(iSubLen);
            }
            sleep(1);
            continue;
        }
        else
        {
            iSubLen += iRc;
            if(iSubLen >= length)
            {
                return(length);
            }
        }
    }
    return -1;
}


